#include <cmath>
#include <iostream>
#include <iomanip>
#include "Function.h"
#include "NumCalc.h"

using std::cout;
using std::endl;

const int DECIMALS_TO_DISPLAY = 16;
const int VERBOSITY = NumCalc::VERBOSITY_NONE;

/**
 * f(x) for Exercise 1, item 1.
 *
 */
class FEx1Item1: public Function {
public:
    double f(double x) const {
        return cos(x) - x;
    }
};

/**
 * fi(x) for Exercise 1, item 1.
 *
 */
class FiEx1Item1: public Function {
public:
    double f(double x) const {
        return x + (cos(x) - x)/(sin(x) + 1);
    }
};

/**
 * f(x) for Exercise 1, item 1.
 *
 */
class FEx1Item2: public Function {
public:
    double f(double x) const {
        return tanh(x);
    }
};

/**
 * fi(x) for Exercise 1, item 1.
 *
 */
class FiEx1Item2: public Function {
public:
    double f(double x) const {
        return x - tanh(x)/(1-pow(tanh(x),2)) ;
    }
};

/**
 * f(x) for Exercise 2, item 1.
 *
 */
class FEx2Item1: public Function {
public:
    double f(double x) const {
        return pow(x,3) - x - 1;
    }
};

/**
 * fi(x) for Exercise 2, item 1.
 *
 */
class FiEx2Item1: public Function {
public:
    double f(double x) const {
        return x - (pow(x,3) - x - 1)/((3*pow(x,2)) - 1);
    }
};

/**
 * f(x) for Exercise 3.
 *
 */
class FEx3: public Function {
public:
    double f(double x) const {
        return pow(x,2) + x - 6;
    }
};

/**ab
 * fi(x) for Exercise 3.
 *
 */
class FiEx3a: public Function {
public:
    double f(double x) const {
        return (-1)*(pow(x,2)) + 6;
    }
};

/**
 * fi(x) for Exercise 3.
 *
 */
class FiEx3b: public Function {
public:
    double f(double x) const {
        return sqrt(6 - x);
    }
};

/**
 * fi(x) for Exercise 3.
 *
 */
class FiEx3c: public Function {
public:
    double f(double x) const {
        return (6/x) - 1;
    }
};

/**
 * fi(x) for Exercise 3.
 *
 */
class FiEx3d: public Function {
public:
    double f(double x) const {
        return -pow((x + 1/2),2) + 25/4;
    }
};


int main(int argc, char** argv) {
    cout << "Exercise 1:" << endl;
    cout << "  function 1:" << endl;
    Result r = NumCalc::bisection(0, 4.7, 0.0000000001, FEx1Item1(), VERBOSITY);
    cout << "    bisection:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;
    r = NumCalc::fixedPoint(2.35, 0.0000000001, FEx1Item1(), FiEx1Item1(), VERBOSITY);
    cout << "    newton:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;
    cout << "  function 2:" << endl;
    r = NumCalc::bisection(-3, 2, 0.0000000001, FEx1Item2(), VERBOSITY);
    cout << "    bisection:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;
    r = NumCalc::fixedPoint(-1, 0.0000000001, FEx1Item2(), FiEx1Item2(), VERBOSITY);
    cout << "    newton:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;

    cout << "Exercise 2:" << endl;
    cout << "  item 1:" << endl;
    r = NumCalc::fixedPoint(0, 0.0000000001, FEx2Item1(), FiEx2Item1(), VERBOSITY);
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;

    cout << "Exercise 3:" << endl;
    cout << "  function b:" << endl;
    r = NumCalc::fixedPoint(0, 0.0000000001, FEx3(), FiEx3b(), VERBOSITY );
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;

    cout << "Exercise 4:" <<endl;
    cout << "   function 1:" << endl;
    r = NumCalc::binewton(0, 4.7, 0.0000000001, FEx1Item1(), FiEx1Item1(), VERBOSITY );
    cout << "    binewton:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;
    cout << "   function 2:" << endl;
    r = NumCalc::binewton(-3, 2, 0.0000000001, FEx1Item2(), FiEx1Item2(), VERBOSITY );
    cout << "    binewton:" << endl;
    cout << "      result: " << std::setprecision(DECIMALS_TO_DISPLAY) << std::fixed << r.x << " +- " << r.error << " (" << r.iterationCount << " iterations)" << ", f(x)=" << r.fx << ((r.check) ? ", checked ok" : ", check error.") << endl;
}

